package com.niodata.dp.task;

import com.niodata.dp.core.exception.InvalidParamException;
import com.niodata.dp.log.LogUtil;
import com.niodata.dp.task.entity.PackageParams;
import com.niodata.dp.task.entity.PluginPackage;
import com.niodata.dp.task.entity.v2.PluginPackageV2;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

public class PluginDocumentGenerator {

  private static final Logger logger = Logger.getLogger(PluginDocumentGenerator.class);

  private static final String UTF8 = "UTF-8";

  static Template template = null;
  static Template pluginMdTemplate = null;
  static Template pluginMdTemplateV2 = null;


  static {
    Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
    cfg.setClassForTemplateLoading(PluginDocumentGenerator.class, "/");
    cfg.setDefaultEncoding("UTF-8");

    try {
      template = cfg.getTemplate("plugin_doc.ftlh");
      pluginMdTemplate = cfg.getTemplate("plugin_md_doc.ftlh");
      pluginMdTemplateV2 = cfg.getTemplate("plugin_md_doc_v2.ftlh");
    } catch (IOException e) {
      LogUtil.logStackTrace(logger, e);
    }
  }

  /**
   * generate plugin document.
   *
   * @param pluginPackage plugin
   * @return document string
   * @throws IOException ioe
   * @throws TemplateException tempe
   */
  public static String genHtmlDocument(PluginPackage pluginPackage)
      throws IOException, TemplateException {
    Map<String, Object> root = new HashMap<>();
    root.put("plugin", pluginPackage);
    root.put("params", pluginPackage.getParams() == null ? Collections.emptyList()
        : pluginPackage.getParams().getParamSchemas());
    root.put("outParams", pluginPackage.getOutParams() == null ? Collections.emptyList() :
        pluginPackage.getOutParams().getParamSchemas());
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    OutputStreamWriter writer = new OutputStreamWriter(bos);
    template.process(root, writer);
    String doc = new String(bos.toByteArray());
    writer.close();
    return doc;
  }


  /**
   * gen markdown doc.
   */
  public static String genUsageMd(PluginPackage pluginPackage)
      throws Exception {
    String pathPrefix = pluginPackage.getPkgPath();
    String usagePath = pathPrefix + File.separator + "doc" + File.separator + "usage.md";
    File usageFile = new File(usagePath);
    if (!usageFile.exists()) {
      logger.error("pathPrefix:" + pathPrefix);
      logger.error("usage.md path:" + usageFile.getAbsolutePath());
      throw new InvalidParamException("usage.md not exists!");
    }

    String usageStr = FileUtils.readFileToString(usageFile, UTF8);
    String usage = PluginPackageParserV2.removeHtmlNote(usageStr);

    if (!usage.contains("功能概述")) {
      throw new InvalidParamException("usage文档必须包含\"功能概述\"");
    }

    if (!usage.contains("使用方法")) {
      throw new InvalidParamException("usage文档必须包含\"使用方法\"");
    }

    PackageParams packageParams = pluginPackage.getParams();
    if (packageParams != null) {
      packageParams.getParamSchemas().forEach(packageParam -> {
        String desc = packageParam.getDescription();
        if (desc.contains("\r\n")) {
          desc = desc.replaceAll("\r\n", "<br>");
        }
        desc = desc.replaceAll("\r", "<br>");
        desc = desc.replaceAll("\n", "<br>");
        packageParam.setDescription(desc);
      });
    }

    Map<String, Object> root = new HashMap<>();
    root.put("plugin", pluginPackage);
    root.put("params", pluginPackage.getParams() == null ? Collections.emptyList()
        : pluginPackage.getParams().getParamSchemas());
    root.put("outParams", pluginPackage.getOutParams() == null ? Collections.emptyList() :
        pluginPackage.getOutParams().getParamSchemas());
    root.put("usage", usage);
    root.put("changeLogs", pluginPackage.getChanges());

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    OutputStreamWriter writer = new OutputStreamWriter(bos);
    try {
      pluginMdTemplate.process(root, writer);
      writer.close();
    } catch (Exception e) {
      LogUtil.logStackTrace(logger, e);
    }
    return new String(bos.toByteArray());
  }

  /**
   * generate markdown usage document.
   * @param pluginPackage pkg
   * @param usageStr usage markdown
   * @return document
   * @throws Exception ex
   */
  public static String genUsageMd(PluginPackageV2 pluginPackage, String usageStr)
      throws Exception {
    String usage = PluginPackageParserV2.removeHtmlNote(usageStr);
    if (!usage.contains("功能概述")) {
      throw new InvalidParamException("usage文档必须包含\"功能概述\"");
    }

    if (!usage.contains("使用方法")) {
      throw new InvalidParamException("usage文档必须包含\"使用方法\"");
    }

    Map<String, Object> root = new HashMap<>();
    root.put("plugin", pluginPackage);
    root.put("paramGroups", pluginPackage.getParams().getGroups());
    root.put("outParams", pluginPackage.getOutParams() == null ? Collections.emptyList() :
        pluginPackage.getOutParams().getParamSchemas());
    root.put("usage", usage);
    root.put("changeLogs", pluginPackage.getChanges());

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    OutputStreamWriter writer = new OutputStreamWriter(bos);
    try {
      pluginMdTemplateV2.process(root, writer);
      writer.close();
    } catch (Exception e) {
      LogUtil.logStackTrace(logger, e);
    }
    return new String(bos.toByteArray());
  }


  public static String genUsageMd(PluginPackageV2 pluginPackage, File useageFile)
      throws Exception {
    FileInputStream in = new FileInputStream(useageFile);
    String usageStr = IOUtils.toString(in, "UTF-8");
    in.close();
    return genUsageMd(pluginPackage, usageStr);
  }
}
